/*
 * -----------------------------------------------------------------------------
 * Project  : MS Message Queue Support
 * Purpose  : Provide support for a base class message queue. I selected to use
 *          : the message-queue API to have the ability to access all MSMQ
 *          : functionality. The provided MSMQ object looks fine still lack of
 *          : access to internals may be a problem on some projects.
 *          : Code was based on provided example from MSMQ SDK.
 * File     : Win32.Net.Msmq.cpp
 * Author   : Eduardo Sobrino
 * Date     : Aug/1998
 * Updated  : Jun/2007  (Added code to Kif framework)
 */

#pragma region   // Include Files

#include "stdafx.h"

#include <stdio.h>
#include "Win32.Net.Msmq.h"

#pragma endregion

// -----------------------------------------------------------------------------

namespace Kif
{
namespace Win32
{
namespace Net
{
namespace Msmq
{

#define INCLUDE_MQUEUE_SUPPORT_
#ifdef INCLUDE_MQUEUE_SUPPORT_

// -----------------------------------------------------------------------------
#pragma region   // Declare MsmqSettings class

void MsmqSettings::Init()
{
   ReadTimeOut = DEFAULT_QUEUE_TIMEOUT;
   *FormatName = (WCHAR)NULL;
   IsTrasactional = false;

   FormatNameLength = 0;
   OutQueueOnly = FALSE;
}  // end of init queue settings...

BOOL MsmqSettings::Init(MsmqQueue *queue)
{
   if (!FormatName[0])
   {
      Kif::Win32::String ostr;
      ostr.Copy(HostName);
      ostr.Copy(TEXT("\\"));
      ostr.Concat(QueueName);

      if (queue->PathToFormat(FormatName,ostr.GetBuffer()) == MsmqStatus_OK)
         FormatNameLength = queue->InquireFormatNumChars();
      else {
         *FormatName = (WCHAR)NULL;
         FormatNameLength = 0;
      }
      if (!(*FormatName))
         return (FALSE);
   }

   queue->SetQueuePathName(HostName.GetBuffer(),QueueName.GetBuffer());
   queue->SetFormat(FormatName,FormatNameLength);

   return(TRUE);
}  // end of setup queue using settings...

BOOL MsmqSettings::Init(MsmqQueue *queue,TCHAR *pFormatName)
{  Init();
   FormatNameLength = (DWORD)Kif::Win32::String::GetLength(pFormatName);
   Kif::Win32::String::Copy(FormatName,FormatNameLength,pFormatName);

   queue->SetFormat(FormatName,FormatNameLength);
   return(TRUE);
}  // end of setup queue using settings...

BOOL MsmqSettings::OpenToSend(MsmqQueue *queue)
{  bool isok = false;
   if (!queue->OpenToSend(FormatName)) {
      *FormatName = NULL;
      Init(queue);
      isok = queue->OpenToSend(FormatName) == TRUE;
   }
   else
      isok = true;
   return(isok == TRUE);
}  // end of open queue to send...

BOOL MsmqSettings::InitAndOpenToSend(MsmqQueue *queue,TCHAR *pFormatName)
{  Init(queue,pFormatName);
   return(queue->OpenToSend(FormatName));
}  // end of open queue to send...

void MsmqSettings::GetErrorString(
   TCHAR *pErrString,int errStrLen,TCHAR *pMess,MsmqQueue *queue)
{
   Kif::Win32::String outErr;

   outErr.Copy(pMess);
   outErr.Concat(TEXT(" "));
   outErr.Concat(HostName);
   outErr.Concat(TEXT("\\"));
   outErr.Concat(QueueName);
   outErr.Concat(TEXT(" ["));
   outErr.Concat(FormatName);
   outErr.Concat(TEXT("] HRESULT("));
   outErr.Concat(queue->LastHResult);

   Kif::Win32::String::Copy(pErrString,(size_t)errStrLen,outErr.GetBuffer());
}  // end of get error string

void MsmqSettings::GetErrorString(
   TCHAR *pErrString,int errStrLen,MsmqQueue *queue)
{
   Kif::Win32::String outErr;
   if ((QueueName.GetLength() > 0) && (HostName.GetLength() > 0))
   {
      outErr.Copy(HostName);
      outErr.Concat(TEXT("\\"));
      outErr.Concat(QueueName);
      outErr.Concat(TEXT(" ["));
      outErr.Concat(FormatName);
      outErr.Concat(TEXT("] HRESULT("));
      outErr.Concat(queue->LastHResult);
   }
   else
   {
      outErr.Concat(TEXT("["));
      outErr.Concat(FormatName);
      outErr.Concat(TEXT("] HRESULT("));
      outErr.Concat(queue->LastHResult);
   }

   Kif::Win32::String::Copy(pErrString,errStrLen,outErr.GetBuffer());
}  // end of get error string

#pragma endregion
// -----------------------------------------------------------------------------
#pragma region   // Declare MsmqQueue class

/*
 * Function : MsmqStatusToString(MsmqStatus insts,TCHAR *obuff,int mlen)
 * Purpose  : Get string related to given MsmqStatus (status code).  It is
 *          : assume that obuff is mlen+1 since this function will insert
 *          : a null char at the end of the string.
 * In       : insts = input status code (MsmqStatus)
 * Out      : obuff = output buffer (TCHAR*)
 * In       : mlen  = output buffer max num of chars (int)
 * Date     : Aug/98  (ESob)
 */

void MsmqStatusToString(MsmqStatus insts,TCHAR *obuff,int mlen)
{
   Kif::Win32::String ostr;

   switch (insts) {
      case MsmqStatus_OK:
         ostr.Copy(TEXT("OK"));
         break;
      case MsmqStatus_CANTCREATE:
         ostr.Copy(TEXT("CAN'T CREATE QUEUE"));
         break;
      case MsmqStatus_CANTRETFNAME:
         ostr.Copy(TEXT("CAN'T RETRIEVE FORMAT NAME"));
         break;
      case MsmqStatus_CANTRETGNAME:
         ostr.Copy(TEXT("CAN'T RETRIEVE FORMAT USING GUID"));
         break;
      case MsmqStatus_QEXISTS:
         ostr.Copy(TEXT("QUEUE ALREADY EXISTS"));
         break;
      case MsmqStatus_QUEUENOTFOUND:
         ostr.Copy(TEXT("QUEUE NOT FOUND"));
         break;
      case MsmqStatus_OPRANOTSUPP:
         ostr.Copy(TEXT("OPERATION NOT SUPPORTED"));
         break;
      case MsmqStatus_OPENRETRY:
         ostr.Copy(TEXT("OPEN QUEUE RETRYING"));
         break;
      case MsmqStatus_CANTROPEN:
         ostr.Copy(TEXT("CAN'T OPEN QUEUE TO RECEIVE"));
         break;
      case MsmqStatus_CANTSOPEN:
         ostr.Copy(TEXT("CAN'T OPEN TO SEND"));
         break;
      case MsmqStatus_RETRYROPENF:
         ostr.Copy(TEXT("RETRY TO OPEN TO RECEIVE FAILED"));
         break;
      case MsmqStatus_NEEDPATH:
         ostr.Copy(TEXT("NEED PATH"));
         break;
      case MsmqStatus_NEEDFORMAT:
         ostr.Copy(TEXT("NEED FORMAT"));
         break;
      case MsmqStatus_ERECEIVING:
         ostr.Copy(TEXT("ERROR RECEIVING MESSAGE"));
         break;
      case MsmqStatus_CANTDELETE:
         ostr.Copy(TEXT("CAN'T DELETE QUEUE"));
         break;
      case MsmqStatus_NOQREGISTERED:
         ostr.Copy(TEXT("NO QUEUE REGISTERED"));
         break;
      case MsmqStatus_ELOCBEGIN:
         ostr.Copy(TEXT("ERROR ON LOCATE BEGIN"));
         break;
      case MsmqStatus_ELOCNEXT:
         ostr.Copy(TEXT("ERROR ON LOCATE NEXT"));
         break;
      case MsmqStatus_CANTSEND:
         ostr.Copy(TEXT("CAN'T SEND MESSAGE"));
         break;
      case MsmqStatus_INVQUEUEID:
         ostr.Copy(TEXT("INVALID QUEUE ID"));
         break;
      case MsmqStatus_ALREADYCONN:
         ostr.Copy(TEXT("ALREADY CONNECTED"));
         break;
      case MsmqStatus_NOMSMQSUPPORT:
         ostr.Copy(TEXT("NO MESSAGE QUEUE SUPPORT"));
         break;
      case MsmqStatus_QUEUENOTOPEN:
         ostr.Copy(TEXT("QUEUE NOT OPEN"));
         break;
      case MsmqStatus_BADMESSFORMAT:
         ostr.Copy(TEXT("BAD MESSAGE FORMAT"));
         break;
      case MsmqStatus_ACCESSDENIED:
         ostr.Copy(TEXT("ACCESS DENIED"));
         break;

      default : ostr.Copy(TEXT("UNKNOWN STATUS CODE"));
   }

   Kif::Win32::String::Copy(obuff,mlen,ostr.GetBuffer());
}  // end of MsmqStatus to string

/*
 * Function : MsmqQueue::MsmqQueue()
 * Purpose  : Init object.
 * Date     : Aug/98  (ESob)
 */

MsmqQueue::MsmqQueue()
{  Init();
   LastHResult = last_hr = 0;
}  // end of init object...

/*
 * Function : MsmqQueue::~MsmqQueue()
 * Purpose  : Release object allocated resources.
 * Date     : Aug/98  (ESob)
 */

MsmqQueue::~MsmqQueue()
{  if (pcMachineName) free(pcMachineName);
}  // end of release object allocated resources.

/*
 * Function : MsmqQueue::InquireFormat(
 * Purpose  : Inquire FormatName from internal format to a TCHAR.
 * Out      : pFormat = format name inquired (TCHAR*)
 *          : iLen    = number of chars in pFormat (int)
 * Date     : Nov/2k  (ESob)
 */

void MsmqQueue::InquireFormat(TCHAR *pFormat,int iLen)
{
   Kif::Win32::String::Copy(pFormat,iLen,wcsFormat);
}  // end of inquire format ...

/*
 * Function : MsmqQueue::SetAdminQueueFormat(TCHAR *aformat)
 * Purpose  : Set administration queue format.
 * Date     : Mar/99  (ESob)
 */

void MsmqQueue::SetAdminQueueFormat(TCHAR *aformat,int buffSize)
{  if (aformat)
      Kif::Win32::String::Copy(wcsAdminFormat,buffSize,aformat);
   else
      *wcsAdminFormat  = (WCHAR)NULL;
}  // end of set administration queue format.

/*
 * Function : MsmqQueue::SetMachineName()
 *          : MsmqQueue::SetMachineName(TCHAR *pmachine)
 * Purpose  : See if the machine name is already gotten, else get it.
 * Date     : Aug/98  (ESob)
 */

void MsmqQueue::SetMachineName()
{  if (!pcMachineName) {
      TCHAR mbsMachineName[MAX_COMPUTERNAME_LENGTH + 1];
      DWORD dwNumChars = MAX_COMPUTERNAME_LENGTH + 1;

      GetComputerName(mbsMachineName, &dwNumChars);
      pcMachineName = *mbsMachineName ?
         Kif::Win32::String::duplicate(mbsMachineName) : TEXT("");
   }
}  // end of set machine name...

void MsmqQueue::SetMachineName(TCHAR *pmachine)
{  if (pcMachineName)
      free(pcMachineName);
   pcMachineName = Kif::Win32::String::duplicate(pmachine);
}  // end of set machine name...

/*
 * Function : MsmqQueue::SetFormat(WCHAR *format,DWORD flen)
 * Purpose  : Set format.
 * In       : format = format to be set (WCHAR*)
 *          : flen   = length of format (unicode) string (DWORD)
 * Date     : Oct/98  (ESob)
 */

void MsmqQueue::SetFormat(WCHAR *format,DWORD flen)
{  //format[flen] = (WCHAR)NULL;
   wcsncpy_s(wcsFormat,format,flen);
   wcsFormat[flen] = (WCHAR)NULL;
   dwFormatNumChars = flen;
}  // end of set format

/*
 * Function : MsmqQueue::SetFormat(TCHAR *format,int flen)
 * Purpose  : Set format.
 * In       : format = format to be set (TCHAR*)
 *          : flen   = length of format (TCHAR) string (int)
 * Date     : Oct/98  (ESob)
 */

void MsmqQueue::SetFormat(TCHAR *format,int flen)
{
   Kif::Win32::String::Copy(wcsFormat,flen,format);
   dwFormatNumChars = flen;
}  // end of set format

/*
 * Function : MsmqQueue::Init()
 * Purpose  : Initialize message queue...
 * Date     : Aug/98  (ESob)
 */

BOOL MsmqQueue::Init()
{  BOOL isOk = TRUE;

   pcMachineName   = NULL;

   Status          = MsmqStatus_OK;
   queueHandle     = NULL;
   Retries         = 4;
   OpenTimeout     = 500;
   RecTimeout      = INFINITE;
   DefaultTimeout  = DEFAULT_QUEUE_TIMEOUT;
  *wcsPathName     = (WCHAR)NULL;
  *wcsFormat       = (WCHAR)NULL;
  *wcsAdminFormat  = (WCHAR)NULL;
   pHandle         = NULL;
   queueCursor     = NULL;
   dwPeekAction    = MQ_ACTION_PEEK_CURRENT;
   dwReceiveAction = MQ_ACTION_RECEIVE;
   MTSTransactional= false;

   UseDeadLetter   = FALSE;
   UseJournal      = FALSE;
   GetAcknowledgements = FALSE;

   SetMachineName();

   return(isOk);
}  // end of init queue...

/*
 * Function : MsmqQueue::SetQueueFullPathName(TCHAR *fpath)
 * Purpose  : Set queue path name.
 * In       : fpath = full path name (TCHAR*)
 * Date     : Mar/99  (ESob)
 */

BOOL MsmqQueue::SetQueueFullPathName(TCHAR *fpath)
{
   Kif::Win32::String::Copy(wcsPathName,MAX_PATH,fpath);
   return(TRUE);
}  // end of set full path name

/*
 * Function : MsmqQueue::SetQueuePathName(TCHAR *qname)
 * Purpose  : Set queue path name.
 * In       : qname = queue name (TCHAR*)
 * Date     : Aug/98  (ESob)
 */

BOOL MsmqQueue::SetQueuePathName(TCHAR *qname)
{
   SetMachineName();

   Kif::Win32::String name;
   name.Copy(pcMachineName);
   name.Concat(TEXT("\\"));
   name.Concat(qname);

   Kif::Win32::String::Copy(wcsPathName,MAX_PATH,name.GetBuffer());
   return(TRUE);
}  // end of set format name

/*
 * Function : MsmqQueue::SetQueuePathName(TCHAR *rmachine,TCHAR *qname)
 * Purpose  : Set queue path name.
 * In       : rmachine = remote machine name (TCHAR*)
 *          : qname = queue name (TCHAR*)
 * Date     : Aug/98  (ESob)
 */

BOOL MsmqQueue::SetQueuePathName(TCHAR *rmachine,TCHAR *qname)
{
   Kif::Win32::String name;
   name.Copy(rmachine);
   name.Concat(TEXT("\\"));
   name.Concat(qname);

   Kif::Win32::String::Copy(wcsPathName,MAX_PATH,name.GetBuffer());
   return(TRUE);
}  // end of set format name

/*
 * Function : MsmqQueue::PathToFormat()
 *          : MsmqQueue::PathToFormat(WCHAR *wFormat,char *path)
 *          : MsmqQueue::PathToFormat(WCHAR *wFormat,WCHAR *wPath)
 * Purpose  : Get the format using given path.
 * Out      : wPath   = private path name already set (WCHAR*)
 *          : wFormat = private format to set (WCHAR*)
 * Date     : Aug/98  (ESob)
 */

MsmqStatus MsmqQueue::PathToFormat()
{  return(PathToFormat(wcsFormat,wcsPathName));
}  // end of convert path to format

MsmqStatus MsmqQueue::PathToFormat(WCHAR *wFormat,char *path)
{
   WCHAR wcsPath[MAX_PATH+1];    // queue pathname
   Kif::Win32::String::Copy(wcsPath,MAX_PATH,path);
   return(PathToFormat(wFormat,wcsPath));
}  // end of path to format...

MsmqStatus MsmqQueue::PathToFormat(WCHAR *wFormat,WCHAR *wPath)
{
   MsmqStatus  rsts;

   dwFormatNumChars = MAX_FORMAT;
   LastHResult = MQPathNameToFormatName(
      wPath,              // IN:     Queue pathname
      wFormat,            // OUT:    Format name
      &dwFormatNumChars); // IN/OUT: Size of format name

   switch (LastHResult) {
      case MQ_OK: rsts = MsmqStatus_OK; break;
      case MQ_ERROR_SERVICE_NOT_AVAILABLE:
         rsts = MsmqStatus_CANTRETFNAME;
         break;
      case MQ_ERROR_ILLEGAL_QUEUE_PATHNAME:
         rsts = MsmqStatus_CANTRETFNAME;
         break;
      case MQ_ERROR_ILLEGAL_FORMATNAME:
         rsts = MsmqStatus_CANTRETFNAME;
         break;
      case MQ_ERROR_NO_DS:
         rsts = MsmqStatus_CANTRETFNAME;
         break;
      case MQ_ERROR_FORMATNAME_BUFFER_TOO_SMALL:
         rsts = MsmqStatus_CANTRETFNAME;
         break;
      case MQ_ERROR_QUEUE_NOT_FOUND:
         rsts = MsmqStatus_QUEUENOTFOUND;
         break;
      default: rsts = MsmqStatus_CANTRETFNAME;
   }

   return((FAILED(LastHResult)) ? MsmqStatus_CANTRETFNAME : MsmqStatus_OK);
}  // end of path to format...

/*
 * Function : MsmqQueue::InquireMachineGUID()
 * Purpose  : Get machine GUID assigned when registering into PEC.
 * Date     : Mar/99  (ESob)
 */

BOOL MsmqQueue::InquireMachineGUID(TCHAR *wszMachineGuid,int buffSize)
{  BOOL isok;

   MQQMPROPS QMProps;
   MQPROPVARIANT Variant;
   MSGPROPID PropId;
   GUID    guidMachineId;
    
   PropId = PROPID_QM_MACHINE_ID;                 //PropId
   Variant.vt = VT_CLSID;                         //Type
   Variant.puuid = &guidMachineId;                //Value
    
   QMProps.cProp = 1;                //Number of properties.
   QMProps.aPropID = &PropId;        //Id of property.
   QMProps.aPropVar = &Variant;      //Value of property.
   QMProps.aStatus  = NULL;          //No Error report.
 
   // Call MQGetMachineProperties to retrieve the machine identifier
   // (PROPID_QM_MACHINE_ID) of the local computer.

   LastHResult = MQGetMachineProperties(
           NULL,
           NULL,
           &QMProps    
           );
   isok = (!FAILED(LastHResult));

   if (isok) {
 
      // Translate the machine GUID into a string.

      TBYTE *pszUuid = 0;
      if(UuidToString(&guidMachineId,(RPC_WSTR*)&pszUuid) != RPC_S_OK)
         isok = FALSE;
      else
      {
         Kif::Win32::String::Copy(wszMachineGuid,buffSize,pszUuid);
         RpcStringFree((RPC_WSTR*)&pszUuid);
         isok = TRUE;
      }
   }

   return(isok);
}  // end of get machine GUID...

/*
 * Function : MsmqQueue::InquireDeadLetterFormat(WCHAR *dlFormat)
 * Purpose  : Inquire the dead letter queue format.
 * Date     : Mar/99  (ESob)
 */

#define MGUID_SIZE 40
BOOL MsmqQueue::InquireDeadLetterFormat(WCHAR *dlFormat,int buffSize)
{
   BOOL isok;
   Kif::Win32::String name;
   TCHAR wszMachineGuid[MGUID_SIZE+1];

   if (InquireMachineGUID(wszMachineGuid,MGUID_SIZE))
   {
      name.Copy(TEXT("MACHINE="));
      name.Concat(wszMachineGuid);
      name.Concat(TEXT(";DEADLETTER"));
      isok = TRUE;
   }
   else
      isok = FALSE;

   Kif::Win32::String::Copy(dlFormat,buffSize,name.GetBuffer());
   return(isok);
}  // end of inquire dead-letter queue format...

/*
 * Function : MsmqQueue::Create(CLSID &guid,TCHAR *qname,TCHAR *descript)
 * Purpose  : Create a queue.
 * In       : guid     = unique queue's identifier (CLSID &)
 *          : qname    = queue name (TCHAR*)
 *          : descript = queue description (TCHAR*)
 * Date     : Aug/98  (ESob)
 */

BOOL MsmqQueue::Create(CLSID &guid,TCHAR *qname,TCHAR *descript)
{  DWORD dwFNumChars;

   MQQUEUEPROPS  qprops;
   MQPROPVARIANT aPropVar[MAX_VAR];
   QUEUEPROPID   aqPropId[MAX_VAR];
   DWORD         cProps;
   WCHAR         wdescript[Kif::Win32::String::MaxStringLength+1];

   // prepare description

   int ilen = (int)Kif::Win32::String::GetLength(descript);
   if (ilen > Kif::Win32::String::MaxStringLength)
      descript[ilen] = (TCHAR)NULL;

   Kif::Win32::String::Copy(
      wdescript,Kif::Win32::String::MaxStringLength,descript);

   // Prepare properties to create a queue on local machine

   cProps = 0;

   // Set the PathName

   SetQueuePathName(qname);

   aqPropId[cProps]         = PROPID_Q_PATHNAME;
   aPropVar[cProps].vt      = VT_LPWSTR;
   aPropVar[cProps].pwszVal = wcsPathName;
   cProps++;

   // Set the type of the queue
   // (Will be used to locate all the queues of this type)

   aqPropId[cProps]         = PROPID_Q_TYPE;
   aPropVar[cProps].vt      = VT_CLSID;
   aPropVar[cProps].puuid   = &guid;
   cProps++;

   // Put a description to the queue
   // (Useful for administration through the MSMQ admin tools)

   aqPropId[cProps]         = PROPID_Q_LABEL;
   aPropVar[cProps].vt      = VT_LPWSTR;
   aPropVar[cProps].pwszVal = wdescript;
   cProps++;

   // Create a QUEUEPROPS structure

   qprops.cProp    = cProps;
   qprops.aPropID  = aqPropId;
   qprops.aPropVar = aPropVar;
   qprops.aStatus  = 0;

   //
   // Create the queue
   //

   dwFNumChars = MAX_FORMAT;
   LastHResult = MQCreateQueue(
      NULL,           // IN:     Default security
      &qprops,        // IN/OUT: Queue properties
      wcsFormat,      // OUT:    Format name (OUT)
      &dwFNumChars);  // IN/OUT: Size of format name

   if (FAILED(LastHResult)) {

      //
      // API Fails, not because the queue exists

      if (LastHResult == MQ_ERROR_QUEUE_EXISTS) {

         //
         // Queue exist, so get its format name

         MsmqStatus rsts = PathToFormat(wcsFormat,wcsPathName);

         Status = (rsts == MsmqStatus_OK) ? MsmqStatus_QEXISTS : rsts;
      }
      else Status = MsmqStatus_CANTCREATE;
   }
   else Status = MsmqStatus_OK;

   return((Status == MsmqStatus_OK) || (Status == MsmqStatus_QEXISTS));
}  // end of create queue...

/*
 * Function : Create(TCHAR *pHost,TCHAR *pQueue,TCHAR *pDescript)
 * Purpose  : Create queue.
 * Date     : Nov/2k  (ESob)
 */

BOOL MsmqQueue::Create(TCHAR *pHost,TCHAR *pQueue,TCHAR *pDescript)
{  SetQueuePathName(pHost,pQueue);
   return(Create(pDescript));
}  // end of create queue ...

/*
 * Function : MsmqQueue::Create(TCHAR *qname,TCHAR *descript)
 * Purpose  : Create a queue.
 * In       : qname    = queue name (TCHAR*)
 *          : descript = queue description (TCHAR*)
 * Date     : Aug/98  (ESob)
 */

BOOL MsmqQueue::Create(TCHAR *qname,TCHAR *pDescript)
{
   SetQueuePathName(qname);
   return(Create(pDescript));
}  // end of create queue given queue name ...

BOOL MsmqQueue::Create(TCHAR *descript)
{  DWORD dwFNumChars;

   MQQUEUEPROPS  qprops;
   MQPROPVARIANT aPropVar[MAX_VAR];
   QUEUEPROPID   aqPropId[MAX_VAR];
   DWORD         cProps;
   WCHAR         wdescript[Kif::Win32::String::MaxStringLength+1];

   // prepare description

   int ilen = (int)Kif::Win32::String::GetLength(descript);
   if (ilen > Kif::Win32::String::MaxStringLength)
      descript[ilen] = (TCHAR)NULL;
   Kif::Win32::String::Copy(
      wdescript,Kif::Win32::String::MaxStringLength,descript);

   // Prepare properties to create a queue on local machine

   cProps = 0;

   // Set the PathName

   aqPropId[cProps]         = PROPID_Q_PATHNAME;
   aPropVar[cProps].vt      = VT_LPWSTR;
   aPropVar[cProps].pwszVal = wcsPathName;
   cProps++;

   // Put a description to the queue
   // (Useful for administration through the MSMQ admin tools)

   aqPropId[cProps]         = PROPID_Q_LABEL;
   aPropVar[cProps].vt      = VT_LPWSTR;
   aPropVar[cProps].pwszVal = wdescript;
   cProps++;

   // Create a QUEUEPROPS structure

   qprops.cProp    = cProps;
   qprops.aPropID  = aqPropId;
   qprops.aPropVar = aPropVar;
   qprops.aStatus  = 0;

   //
   // Create the queue
   //

   dwFNumChars = MAX_FORMAT;
   LastHResult = MQCreateQueue(
      NULL,           // IN:     Default security
      &qprops,        // IN/OUT: Queue properties
      wcsFormat,      // OUT:    Format name (OUT)
      &dwFNumChars);  // IN/OUT: Size of format name

   if (FAILED(LastHResult)) {

      //
      // API Fails, not because the queue exists

      if (LastHResult == MQ_ERROR_QUEUE_EXISTS) {

         //
         // Queue exist, so get its format name

         MsmqStatus rsts = PathToFormat(wcsFormat,wcsPathName);

         Status = (rsts == MsmqStatus_OK) ? MsmqStatus_QEXISTS : rsts;
      }
      else Status = MsmqStatus_CANTCREATE;
   }
   else
      Status = MsmqStatus_OK;

   return((Status == MsmqStatus_OK) || (Status == MsmqStatus_QEXISTS));
}  // end of create queue...

/*
 * Function : MsmqQueue::OpenToPeek(WCHAR *format)
 * Purpose  : Open to peek queue.
 * In       : format = queue format to open for peeking... (WCHAR*)
 * Date     : May/99  (ESob)
 */

BOOL MsmqQueue::OpenToPeek(WCHAR *format)
{

   OpenToReceive(format,(DWORD)wcslen(format));

   if (Status == MsmqStatus_OK)
   {
      LastHResult = MQCreateCursor(queueHandle,&pHandle);
      dwPeekAction = MQ_ACTION_PEEK_CURRENT;
   }

   return (Status == MsmqStatus_OK);
}  // end of open queue to peek messages

/*
 * Function : MsmqQueue::PeekMessage(TCHAR *lbl,int mllen,TCHAR *buff,int mblen)
 * Purpose  : Peek message.
 * Out      : lbl   = received label (TCHAR*)
 * In       : mllen = maximum label size (int)
 * Out      : buff  = received buffer (TCHAR*)
 * In       : mblen = maximum buffer length (int)
 * Date     : Aug/98  (ESob)
 */

BOOL MsmqQueue::PeekMessage(TCHAR *lbl,int mllen,TCHAR *buff,int mblen)
{
   queueCursor     = pHandle;
   dwReceiveAction = dwPeekAction;
   dwPeekAction    = MQ_ACTION_PEEK_NEXT;
   ReceiveMessage(lbl,mllen,buff,mblen);

   return (Status == MsmqStatus_OK);
}  // end of peek message...

/*
 * Function : MsmqQueue::PeekCurrentMessage(
 *          :    TCHAR *lbl,int mllen,TCHAR *buff,int mblen)
 * Purpose  : Peek message.
 * Out      : lbl   = received label (TCHAR*)
 * In       : mllen = maximum label size (int)
 * Out      : buff  = received buffer (TCHAR*)
 * In       : mblen = maximum buffer length (int)
 * Date     : Aug/98  (ESob)
 */

BOOL MsmqQueue::PeekCurrentMessage(TCHAR *lbl,int mllen,TCHAR *buff,int mblen)
{
   queueCursor     = pHandle;
   dwReceiveAction = dwPeekAction;
   dwPeekAction    = MQ_ACTION_PEEK_CURRENT;
   ReceiveMessage(lbl,mllen,buff,mblen);

   return (Status == MsmqStatus_OK);
}  // end of peek current message...

/*
 * Function : MsmqQueue::ReadMessage(TCHAR *lbl,int mllen,TCHAR *buff,int mblen)
 * Purpose  : Read message.
 * Out      : lbl   = received label (TCHAR*)
 * In       : mllen = maximum label size (int)
 * Out      : buff  = received buffer (TCHAR*)
 * In       : mblen = maximum buffer length (int)
 * Date     : Aug/98  (ESob)
 */

BOOL MsmqQueue::ReadMessage(TCHAR *lbl,int mllen,TCHAR *buff,int mblen)
{
   queueCursor     = pHandle;
   dwReceiveAction = MQ_ACTION_RECEIVE;
   dwPeekAction    = MQ_ACTION_PEEK_CURRENT;
   ReceiveMessage(lbl,mllen,buff,mblen);

   return (Status == MsmqStatus_OK);
}  // end of peek message...

/*
 * Function : MsmqQueue::OpenToReceive(TCHAR *fname,int retries,long wsec)
 *          : MsmqQueue::OpenToReceive(int retries,long wsec)
 *          : MsmqQueue::OpenToReceive(WCHAR *format,DWORD flen)
 *          : MsmqQueue::OpenToReceive()
 * Purpose  : Open to receive messages.
 * In       : qname   = queue name (TCHAR*)
 *          : wmsec   = wait for open in milliseconds (long)
 *          : retries = number of retries (int)
 * Date     : Aug/98  (ESob)
 */

BOOL MsmqQueue::OpenToReceive(TCHAR *qname,long wmsec,int retries)
{  // set path using given the queue name

   SetQueuePathName(qname);

   // try to convert queue path to a format

   Status = PathToFormat(wcsFormat,wcsPathName);

   // if format was found, then try to open queue to receive messages

   return((Status == MsmqStatus_OK) ? OpenToReceive(wmsec,retries) : FALSE);
}  // end of open queue to receive messages

BOOL MsmqQueue::OpenToReceive(WCHAR *format,DWORD flen)
{  wcsncpy_s(wcsFormat,flen+1,format,flen);
   wcsFormat[flen]  = (DWORD)NULL;
   dwFormatNumChars = flen;
   return(OpenToReceive(OpenTimeout,Retries));
}  // end of open queue to recieve messages

BOOL MsmqQueue::OpenToReceive()
{  Status = PathToFormat(wcsFormat,wcsPathName);
   return(OpenToReceive(OpenTimeout,Retries));
}  // end of open queue to receive messages

BOOL MsmqQueue::OpenToReceiveWithFormat(TCHAR *pFormat)
{
   // convert given string to a wide-string
   Kif::Win32::String::Copy(wcsFormat,MAX_FORMAT,pFormat);
   return(OpenToReceive(OpenTimeout,Retries));
}  // end of open queue to receive messages

BOOL MsmqQueue::OpenToReceive(long wmsec,int retries)
{
   // check that we have a path / format
   if (!(*wcsPathName) && !(*wcsFormat))
   {
      Status = MsmqStatus_NEEDPATH;
      return(FALSE);
   }

   if (!(*wcsFormat))
   {
      Status = MsmqStatus_NEEDFORMAT;
      return(FALSE);
   }

   // Open the queue for receive access
   LastHResult = MQOpenQueue(
      wcsFormat,          // IN:  Queue format name
      MQ_RECEIVE_ACCESS,  // IN:  Want to receive from queue
      0,                  // IN:  Allow sharing
      &queueHandle);      // OUT: Handle of open queue

   //
   // Little bit tricky. MQCreateQueue succeeded but it does not mean
   // that MQOpenQueue will, because of replication delay. The queue is
   // registered in MQIS, but it might take a replication interval
   // until the replica reach the server I am connected to.
   // To overcome this, open the queue in a loop.
   //
   // (in this specific case, this can happen only if this
   //  program is run on a Backup Server Controller - BSC, or on
   //  a client connected to a BSC)

   int count = 0;
   while (LastHResult == MQ_ERROR_QUEUE_NOT_FOUND)
   {
      Status = MsmqStatus_OPENRETRY;

      // Wait a bit
      Sleep(wmsec);

      // And retry
      LastHResult = MQOpenQueue(wcsFormat, MQ_RECEIVE_ACCESS, 0, &queueHandle);
      count++;

      if (count > retries) break;
   }

   if (LastHResult != MQ_ERROR_QUEUE_NOT_FOUND)
      Status = (FAILED(LastHResult)) ? MsmqStatus_CANTROPEN : MsmqStatus_OK;
   else Status = MsmqStatus_RETRYROPENF;

   return (Status == MsmqStatus_OK);
}  // end of open queue to receive messages

/*
 * Function : MsmqQueue::ReceiveMessage(TCHAR *lbl,int mllen,TCHAR *buff,int mblen)
 *          : MsmqQueue::ReceiveMessage(TCHAR *buff,int mblen)
 * Purpose  : Receive message.
 * Out      : lbl   = received label (TCHAR*)
 * In       : mllen = maximum label size (int)
 * Out      : buff  = received buffer (TCHAR*)
 * In       : mblen = maximum buffer length (int)
 * Date     : Aug/98  (ESob)
 */

BOOL MsmqQueue::ReceiveMessage(TCHAR *buff,int mblen)
{  return(ReceiveMessage(NULL,0,buff,mblen));
}  // end of receive message...

BOOL MsmqQueue::ReceiveMessage(TCHAR *lbl,int mllen,TCHAR *buff,int mblen)
{

   MQMSGPROPS    msgprops;
   MQPROPVARIANT aPropVar[MAX_VAR];
   MSGPROPID     amPropId[MAX_VAR];
   DWORD         cProps;
   int           ackIndex;

   WCHAR         wcsMsgLabel[MQ_MAX_MSG_LABEL_LEN];

   // init inputed buffers...

   if (lbl)
      Kif::Win32::String::Clear((TCHAR*)lbl,mllen,NULL);
   Kif::Win32::String::Clear((TCHAR*)buff,mblen,NULL);

   // Prepare message properties to read

   cProps = 0;

   // Set delivery mode to recovable

   amPropId[cProps]      = PROPID_M_DELIVERY;
   aPropVar[cProps].vt   = VT_UI1;
   aPropVar[cProps].bVal = MQMSG_DELIVERY_RECOVERABLE;
   cProps++;

   // Ask for the body of the message

   amPropId[cProps]             = PROPID_M_BODY;
   #ifdef _UNICODE
      aPropVar[cProps].vt       = VT_UI2 | VT_VECTOR;
   #else
      aPropVar[cProps].vt       = VT_UI1 | VT_VECTOR;
   #endif
   aPropVar[cProps].caub.cElems = mblen;
   aPropVar[cProps].caub.pElems = (UCHAR*)buff;
   cProps++;

   // Ask for the label of the message

   amPropId[cProps]         = PROPID_M_LABEL;
   aPropVar[cProps].vt      = VT_LPWSTR;
   aPropVar[cProps].pwszVal = wcsMsgLabel;
   cProps++;

   // Ask for the length of the label of the message

   amPropId[cProps]         = PROPID_M_LABEL_LEN;
   aPropVar[cProps].vt      = VT_UI4;
   aPropVar[cProps].ulVal   = MQ_MAX_MSG_LABEL_LEN;
   cProps++;

   // Provide to send none-delivered messages to the DEADLETTER queue...

   if (UseJournal)
   {
      amPropId[cProps]      = PROPID_M_JOURNAL;
      aPropVar[cProps].vt   = VT_NULL;
      cProps++;
   }

   // Provide to request for acknowledgements...

   if (GetAcknowledgements)
   {
      amPropId[cProps]      = PROPID_M_ACKNOWLEDGE;
      aPropVar[cProps].vt   = VT_NULL;
      cProps++;

      // retrieve acknowledgement field...

      amPropId[cProps]      = PROPID_M_CLASS;
      aPropVar[cProps].vt   = VT_UI2;
      ackIndex = cProps;
      cProps++;
   }
   else
      ackIndex = -1;

   // Create a MSGPROPS structure

   msgprops.cProp    = cProps;
   msgprops.aPropID  = amPropId;
   msgprops.aPropVar = aPropVar;
   msgprops.aStatus  = 0;

   //
   // Receive the message

   RecTimeout = DefaultTimeout;
   LastHResult = MQReceiveMessage(
      queueHandle,       // IN:     Queue handle
      RecTimeout,        // IN:     Timeout
      dwReceiveAction,   // IN:     Read operation
      &msgprops,         // IN/OUT: Message properties to receive
      NULL,              // IN/OUT: No overlap
      NULL,              // IN:     No callback
      queueCursor,       // IN:     No cursor
      NULL);             // IN:     Not part of a transaction

   Status = (FAILED(LastHResult)) ? MsmqStatus_ERECEIVING : MsmqStatus_OK;
   dwReceiveAction = MQ_ACTION_RECEIVE;
   queueCursor = NULL;

   //
   // Copy label if any is given and label-buffer is given...

   if ((Status == MsmqStatus_OK) && lbl && (mllen > 0))
   {
      int ilen = (int)wcslen(wcsMsgLabel);
      if (ilen > mllen)
         wcsMsgLabel[mllen] = (WCHAR)NULL;
      Kif::Win32::String::Copy(lbl,mllen,wcsMsgLabel);

      // prepare acknowledgement message if needed...

      if (ackIndex > 0)
      {
         if ((aPropVar[ackIndex].uiVal == MQMSG_CLASS_ACK_REACH_QUEUE) ||
             (aPropVar[ackIndex].uiVal == MQMSG_CLASS_ACK_RECEIVE))
            Kif::Win32::String::Copy((TCHAR*)buff,mblen,TEXT("ACK"));
         else
         {
            TCHAR *reason;
            switch(aPropVar[ackIndex].uiVal)
            {
               case MQMSG_CLASS_NORMAL :
                  reason = TEXT("NORMAL");
                  break;
               case MQMSG_CLASS_REPORT :
                  reason = TEXT("REPORT");
                  break;
               case MQMSG_CLASS_ACK_REACH_QUEUE :
                  reason = TEXT("ACK_REACH_QUEUE");
                  break;
               case MQMSG_CLASS_ACK_RECEIVE :
                  reason = TEXT("ACK_RECEIVE");
                  break;
               case MQMSG_CLASS_NACK_ACCESS_DENIED :
                  reason = TEXT("ACCESS_DENIED");
                  break;
               case MQMSG_CLASS_NACK_BAD_DST_Q :
                  reason = TEXT("BAD_DST_Q");
                  break;
               case MQMSG_CLASS_NACK_BAD_ENCRYPTION :
                  reason = TEXT("BAD_ENCRYPTION");
                  break;
               case MQMSG_CLASS_NACK_BAD_SIGNATURE :
                  reason = TEXT("BAD_SIGNATURE");
                  break;
               case MQMSG_CLASS_NACK_COULD_NOT_ENCRYPT :
                  reason = TEXT("COULD_NOT_ENCRYPT");
                  break;
               case MQMSG_CLASS_NACK_HOP_COUNT_EXCEEDED :
                  reason = TEXT("HOP_COUNT_EXCEEDED");
                  break;
               case MQMSG_CLASS_NACK_Q_EXCEED_QUOTA :
                  reason = TEXT("Q_EXCEED_QUOTA");
                  break;
               case MQMSG_CLASS_NACK_REACH_QUEUE_TIMEOUT :
                  reason = TEXT("REACH_QUEUE_TIMEOUT");
                  break;
               case MQMSG_CLASS_NACK_PURGED :
                  reason = TEXT("PURGED");
                  break;
               case MQMSG_CLASS_NACK_NOT_TRANSACTIONAL_Q :
                  reason = TEXT("NOT_TRANSACTIONAL_Q");
                  break;
               case MQMSG_CLASS_NACK_NOT_TRANSACTIONAL_MSG :
                  reason = TEXT("NOT_TRANSACTIONAL_MSG");
                  break;
               case MQMSG_CLASS_NACK_Q_DELETED :
                  reason = TEXT("Q_DELETED");
                  break;
               case MQMSG_CLASS_NACK_Q_PURGED :
                  reason = TEXT("Q_PURGED");
                  break;
               case MQMSG_CLASS_NACK_RECEIVE_TIMEOUT :
                  reason = TEXT("RECEIVE_TIMEOUT");
                  break;
               default : reason = TEXT("UNKNOWN");
            }

            Kif::Win32::String outBuff;
            outBuff.Copy(TEXT("NAK : "));
            outBuff.Concat(reason);
            Kif::Win32::String::Copy(buff,mblen,outBuff.GetBuffer());
         }
      }
   }
   else
   {
      switch (LastHResult)
      {
         case MQ_ERROR_ACCESS_DENIED:
            Status = MsmqStatus_ACCESSDENIED;
            break;
         case MQ_ERROR_IO_TIMEOUT:
            Status = MsmqStatus_ERECEIVING;
            break;
         case MQ_ERROR_INVALID_PARAMETER:
            Status = MsmqStatus_ERECEIVING;
            break;
         default:
            Status = MsmqStatus_ERECEIVING;
      }
   }

   last_hr = LastHResult;

   return (Status == MsmqStatus_OK);
}  // end of receive message...

/*
 * Function : MsmqQueue::Delete()
 * Purpose  : Delete queue.
 * Date     : Aug/98  (ESob)
 */


BOOL MsmqQueue::Delete(TCHAR *pHost,TCHAR *pQueue)
{  SetQueuePathName(pHost,pQueue);
   return(Delete());
}  // end of create queue ...

BOOL MsmqQueue::Delete(TCHAR *qname)
{
   WCHAR wcsPName[MAX_PATH+1];
   WCHAR wcsFormat[MAX_FORMAT+1];

   SetMachineName();
   Kif::Win32::String name;
   name.Copy(pcMachineName);
   name.Concat(TEXT("\\"));
   name.Concat(qname);

   Kif::Win32::String::Copy(wcsPName,MAX_FORMAT,name.GetBuffer());

   if (PathToFormat(wcsFormat,wcsPName) != MsmqStatus_OK)
      return (FALSE);

   return(Delete());
}  // end of delete queue ..

BOOL MsmqQueue::Delete()
{
   LastHResult = MQDeleteQueue(wcsFormat);
   Status = (FAILED(LastHResult)) ? MsmqStatus_CANTDELETE : MsmqStatus_OK;

   return(Status == MsmqStatus_OK);
}  // end of delete queue...

/*
 * Function : MsmqQueue::OpenToSend(WCHAR *wFormat)
 *          : MsmqQueue::OpenToSendWithQueueName(TCHAR *qname)
 *          : MsmqQueue::OpenToSend(TCHAR *rmachine,TCHAR *qname)
 *          : MsmqQueue::OpenToSendWithFormat(TCHAR *pFormat)
 * Purpose  : Open queue to send messages using given format.
 * In       : wFormat = format name used to open required queue (WCHAR*)
 *          : qname   = name of queue to open (TCHAR*)
 * Date     : Aug/98  (ESob)
 */

BOOL MsmqQueue::OpenToSend(WCHAR *wFormat)
{
   LastHResult = MQOpenQueue(
      wFormat,           // IN:  Queue format name
      MQ_SEND_ACCESS,    // IN:  Want to send to queue
      0,                 // IN:  Must be 0 for send access
      &queueHandle);     // OUT: Handle of open queue

   //TCHAR ostr[MINSTRLEN];
   //sprintf(ostr,"OpenToSend: Handle[%ld]",qHandle);
   //Print(ostr);

   Status = (FAILED(LastHResult)) ? MsmqStatus_CANTSOPEN : MsmqStatus_OK;
   return(Status == MsmqStatus_OK);
}  // end of open to send...

BOOL MsmqQueue::OpenToSendWithQueueName(TCHAR *qname)
{  // set path using given the queue name

   SetQueuePathName(qname);

   // try to convert queue path to a format

   Status = PathToFormat(wcsFormat,wcsPathName);

   // if format was found, then try to open queue to receive messages

   return((Status == MsmqStatus_OK) ? OpenToSend(wcsFormat) : FALSE);
}  // end of open queue to receive messages

BOOL MsmqQueue::OpenToSendWithFormat(TCHAR *pFormat,int buffSize)
{  // convert given string to a wide-string

   dwFormatNumChars = (DWORD)Kif::Win32::String::GetLength(pFormat);
   Kif::Win32::String::Copy(wcsFormat,buffSize,pFormat);
   wcsFormat[dwFormatNumChars] = (WCHAR)NULL;

   // open to send using given format

   return(OpenToSend(wcsFormat));
}  // end of open queue to receive messages

BOOL MsmqQueue::OpenToSend(TCHAR *rmachine,TCHAR *qname)
{  // set path using given the queue name

   SetQueuePathName(rmachine,qname);

   // try to convert queue path to a format

   Status = PathToFormat(wcsFormat,wcsPathName);

   // if format was found, then try to open queue to receive messages

   return((Status == MsmqStatus_OK) ? OpenToSend(wcsFormat) : FALSE);
}  // end of open queue to receive messages

/*
 * Function : MsmqQueue::Locate(CLSID &guid)
 * Purpose  : Locate queue. For now only one (the first) will be located.
 * In       : guid = unique queue name (GLSID&)
 * Date     : Aug/98  (ESob)
 */

BOOL MsmqQueue::Locate(CLSID &guid)
{

   DWORD         cProps;
   DWORD         cQueue;
   DWORD         dwNumChars;

   MQPROPVARIANT aPropVar[MAX_VAR];
   QUEUEPROPID   aqPropId[MAX_VAR];

   HANDLE        hEnum;

   MQPROPERTYRESTRICTION aPropRestriction[MAX_VAR];
   MQRESTRICTION Restriction;
   MQCOLUMNSET   Column;

   //
   // Prepare parameters to locate a queue

   //
   // 1. Restriction = Queues with PROPID_TYPE = MSMQTest queue type

   cProps = 0;
   aPropRestriction[cProps].rel         = PREQ;
   aPropRestriction[cProps].prop        = PROPID_Q_TYPE;
   aPropRestriction[cProps].prval.vt    = VT_CLSID;
   aPropRestriction[cProps].prval.puuid = &guid;
   cProps++;

   Restriction.cRes      = cProps;
   Restriction.paPropRes = aPropRestriction;

   //
   // 2. Columnset (i.e. queue properties to retrieve) = queue instance

   cProps = 0;
   aqPropId[cProps] = PROPID_Q_INSTANCE;
   cProps++;

   Column.cCol = cProps;
   Column.aCol = aqPropId;

   //
   // Locate the queues. Issue the query

   LastHResult = MQLocateBegin(
      NULL,          // IN:  Context must be NULL
      &Restriction,  // IN:  Restriction
      &Column,       // IN:  Columns (properties) to return
      NULL,          // IN:  No need to sort
      &hEnum);       // OUT: Enumeration handle

   if (FAILED(LastHResult))
   {
      Status = MsmqStatus_ELOCBEGIN;
      return(FALSE);
   }

   //
   // Get the results (up to MAX_VAR)

   cQueue = MAX_VAR;
   LastHResult = MQLocateNext(
      hEnum,         // IN:     Enumeration handle
      &cQueue,       // IN/OUT: Count of properties
      aPropVar);     // OUT:    Properties of located queues

   if (FAILED(LastHResult))
   {
      Status = MsmqStatus_ELOCNEXT;
      return(FALSE);
   }

   //
   // And that's it for locate

   LastHResult = MQLocateEnd(hEnum);

   if (cQueue == 0)
   {

      //
      // Could Not find any queue, so exit

      Status = MsmqStatus_NOQREGISTERED;
      return(FALSE);
   }

   //
   // Open a handle for each of the queues found

   DWORD count;
   for (count = 0; count < cQueue; count++)
   {

      // Convert the queue instance to a format name

      dwNumChars = MAX_FORMAT;
      LastHResult = MQInstanceToFormatName(
         aPropVar[count].puuid, // IN:     Queue instance
         wcsFormat,             // OUT:    Format name
         &dwNumChars);          // IN/OUT: Size of format name

      Status = (FAILED(LastHResult)) ? MsmqStatus_CANTRETGNAME : MsmqStatus_OK;

      //
      // Free the GUID memory that was allocated during the locate.

      MQFreeMemory(aPropVar[count].puuid);
   }

   last_hr = LastHResult;

   return (Status == MsmqStatus_OK);
}  // end of locate queues...

/*
 * Function : MsmqQueue::SendMessage(TCHAR *lbl,int mllen,TCHAR *buff,int mblen)
 *          : MsmqQueue::SendMessage(TCHAR *buff,int mblen)
 * Purpose  : Send given buffer.
 * In       : lbl   = received label (TCHAR*)
 *          : buff  = received buffer (TCHAR*)
 *          : mblen = buffer length (int)
 * Date     : Aug/98  (ESob)
 */

BOOL MsmqQueue::SendMessage(TCHAR *lbl,TCHAR *buff,int mblen)
{

   DWORD         cProps;

   MQMSGPROPS    msgprops;
   MQPROPVARIANT aPropVar[MAX_VAR+1];
   MSGPROPID     amPropId[MAX_VAR+1];

   WCHAR         wcsMsgLabel[MQ_MAX_MSG_LABEL_LEN+1];

   // prepare label
   Kif::Win32::String::Copy(wcsMsgLabel,MQ_MAX_MSG_LABEL_LEN,lbl);

   // Prepare properties of message to send

   cProps = 0;

   // Set delivery mode to recovable

   amPropId[cProps]      = PROPID_M_DELIVERY;
   aPropVar[cProps].vt   = VT_UI1;
   aPropVar[cProps].bVal = MQMSG_DELIVERY_RECOVERABLE;
   cProps++;

   // Set the body of the message

   amPropId[cProps]             = PROPID_M_BODY;
   #ifdef _UNICODE
      aPropVar[cProps].vt       = VT_UI2 | VT_VECTOR;
   #else
      aPropVar[cProps].vt       = VT_UI1 | VT_VECTOR;
   #endif
   aPropVar[cProps].caub.cElems = mblen;
   aPropVar[cProps].caub.pElems = (UCHAR*)buff;
   cProps++;

   //TCHAR ostr[MINSTRLEN];
   //sprintf(ostr,"qhandle[%ld]",qHandle);
   //Print(ostr);
   //sprintf(ostr,"BODY: buff[%d] (%s)",mblen,(TCHAR*)buff);
   //Print(ostr);

   // Set the label of the message

   amPropId[cProps]            = PROPID_M_LABEL;
   aPropVar[cProps].vt         = VT_LPWSTR;
   aPropVar[cProps].pwszVal    = wcsMsgLabel;
   cProps++;

   // Provide to do journaling...

   if (UseJournal)
   {
      amPropId[cProps]         = PROPID_M_JOURNAL;
      aPropVar[cProps].vt      = VT_UI1;
      aPropVar[cProps].bVal    = MQMSG_JOURNAL;
      cProps++;

      //xsprintf(ostr,"USING JOURNALING");
      //Print(ostr);
   }

   // Provide to send none-delivered messages to the DEADLETTER queue...

   if (UseDeadLetter)
   {
      amPropId[cProps]         = PROPID_M_JOURNAL;
      aPropVar[cProps].vt      = VT_UI1;
      aPropVar[cProps].bVal    =
         (UseJournal) ? MQMSG_DEADLETTER | MQMSG_JOURNAL : MQMSG_DEADLETTER;
      cProps++;

      //sprintf(ostr,"USING DEADLETTER");
      //Print(ostr);
   }

   // is there a need for acknowledgement, to manage admin-queue ?

   if (*wcsAdminFormat)
   {

      // set acknowledgement type...

      amPropId[cProps]         = PROPID_M_ACKNOWLEDGE;
      aPropVar[cProps].vt      = VT_UI1;
      aPropVar[cProps].bVal    = MQMSG_ACKNOWLEDGMENT_FULL_REACH_QUEUE;
      cProps++;

      // set queue where acknowledgements will be sent...

      amPropId[cProps]         = PROPID_M_ADMIN_QUEUE;
      aPropVar[cProps].vt      = VT_LPWSTR;
      aPropVar[cProps].pwszVal = wcsAdminFormat;
      cProps++;

      //sprintf(ostr,"USING ADMIN-QUEUE");
      //Print(ostr);
   }

   // Create a MSGPROPS structure

   msgprops.cProp    = cProps;
   msgprops.aPropID  = amPropId;
   msgprops.aPropVar = aPropVar;
   msgprops.aStatus  = 0;

   //
   // Send the message to all the queue

   LastHResult = MQSendMessage(
      queueHandle, // IN: Queue handle
      &msgprops,   // IN: Message properties to send
      MTSTransactional ? MQ_MTS_TRANSACTION : NULL);

   Status = (FAILED(LastHResult)) ? MsmqStatus_CANTSEND : MsmqStatus_OK;

   last_hr = LastHResult;
   return(Status == MsmqStatus_OK);
}  // end of send message...

#pragma endregion

#endif

// -----------------------------------------------------------------------------

}  // end of Kif::Win32::Net::Msmq
}  // end of Kif::Win32::Net
}  // end of Kif::Win32
}  // end of Kif

